import socket
import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from tornado.httpserver import HTTPServer
import queue
import gevent

Queue = queue.Queue()

class HTTPHandler(RequestHandler):
    SUPPORTED_METHODS = ("CONNECT", "GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS")

    @tornado.web.asynchronous
    def get(self):
        print(self.request.method,self.request.host)
        def handle_request(response):
            if response.error and not isinstance(response.error, tornado.httpclient.HTTPError):
                print("Error:%s"%response.error)
            else:
                self.write(response.body)
            self.finish('')
        req = HTTPRequest(
                  url = self.request.uri,
                  method = self.request.method,
                  headers = self.request.headers,
                  body = self.request.body,
                  allow_nonstandard_methods = True,
                  follow_redirects = False,
                  validate_cert = True)

        http_client = AsyncHTTPClient()
        try:
            http_client.fetch(req, handle_request)
        except Exception as e:
            print(e)

    @tornado.web.asynchronous
    def connect(self):
        def read_from_client(data):
            upstream.write(data)

        def read_from_upstream(data):
            client.write(data)

        def client_close(data=None):
            if upstream.closed():
                return
            if data:
                upstream.write(data)
            upstream.close()

        def upstream_close(data=None):
            if client.closed():
                return
            if data:
                client.write(data)
            client.close()

        def start_tunnel():
            print('CONNECT tunnel established to %s'%self.request.uri)
            client.read_until_close(client_close, read_from_client)
            upstream.read_until_close(upstream_close, read_from_upstream)
            client.write(b'HTTP/1.0 200 Connection established\r\n\r\n')

        def on_proxy_response(data=None):
            if data:
                first_line = data.splitlines()[0]
                http_v, status, text = first_line.split(None, 2)
                if int(status) == 200:
                    start_tunnel()
                    return
            self.set_status(500)
            self.finish()

        def start_proxy_tunnel():
            upstream.write('CONNECT %s HTTP/1.1\r\n' % self.request.uri)
            upstream.write('Host: %s\r\n' % self.request.uri)
            upstream.write('Proxy-Connection: Keep-Alive\r\n\r\n')
            upstream.read_until('\r\n\r\n', on_proxy_response)

        host,port = self.request.uri.split(':')
        client = self.request.connection.stream
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        upstream = tornado.iostream.IOStream(sock)
        upstream.connect((host, int(port)), start_tunnel)


    @tornado.web.asynchronous
    def post(self):
        return self.get()
    @tornado.web.asynchronous
    def head(self):
        return self.get()
    @tornado.web.asynchronous
    def delete(self):
        return self.get()
    @tornado.web.asynchronous
    def patch(self):
        return self.get()
    @tornado.web.asynchronous
    def put(self):
        return self.get()
    @tornado.web.asynchronous
    def options(self):
        return self.get()

if __name__ == "__main__":
    app = Application([(r".*", HTTPHandler),])
    httpsServer = HTTPServer(app,
        ssl_options = {
        "certfile": "./ca.crt",
        "keyfile": "./ca.key",}
    )
    app.listen(8088)
    tornado.ioloop.IOLoop.instance().start()